import { getCompanyList } from '@/api/company'
import type { CompanyDto } from '@/api/company/types'
import { getReceiptList } from '@/api/customer'
import type { AntTreeDto } from '@/api/types'
import type { UploadFileDto } from '@/components/file/Upload'
import type { OptionDto } from '@/components/low-code/types'
import type { AnyObject } from '@/types'
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import type { AntdIconType } from '@ant-design/icons-vue/lib/components/AntdIcon'
import { Modal, type ModalFuncProps } from 'ant-design-vue'
import type { VueNode } from 'ant-design-vue/es/_util/type'
import { createVNode } from 'vue'

/**
 * 对象数组排序
 * @param arr
 * @param filed
 */
export function sortObjArr(arr: any[], filed: string) {
    function compare(filed: string) {
        return function (a: any, b: any) {
            const value1 = a[filed]
            const value2 = b[filed]
            return value1 - value2
        }
    }

    return arr.sort(compare(filed))
}

/**
 * JSON 数组去重
 * @param arr
 * @param field
 */
export function jsonArrUnique(arr: any[], field: string) {
    const obj: {
        [key: string]: string | number | boolean
    } = {}
    const result: any[] = []
    arr.forEach((item) => {
        if (!obj[field]) {
            result.push(item)
            obj[field] = true
        }
    })
    return result
}

/**
 * 去掉无意义的0
 * @param num
 * @returns
 */
export function removeZero(num: string) {
    const result = parseFloat(parseFloat(num).toFixed(2))
    return result
}

/**
 * 格式化文件大小
 * @param size
 * @returns
 */
export function fileSizeFormat(size: number | string) {
    const kb = Number(size) / 1024
    if (kb < 1000) return removeZero(kb as unknown as string) + 'KB'
    const M = kb / 1024
    if (M < 1000) return removeZero(M as unknown as string) + 'MB'
    const G = M / 1024
    return removeZero(G as unknown as string) + 'GB'
}

/**
 * @description 时间格式化
 * @param {String} 格式化字符串 格式如：YYYY-mm-dd HH:MM:SS
 * @param {Date} date对象
 * @return {String}
 */
export function dateFormat(
    date: Date | string | number,
    fmt = 'YYYY-MM-DD HH:mm' as string
) {
    if (!date) return '-'
    const ndata: Date = ['string', 'number'].includes(typeof (date)) ? new Date(date) : date as Date
    let ret
    const opt: { [key: string]: string } = {
        'Y+': ndata?.getFullYear().toString(), // 年
        'y+': ndata?.getFullYear().toString(), // 年
        'M+': (ndata?.getMonth() + 1).toString(), // 月
        'D+': ndata?.getDate().toString(), // 日
        'h+': ndata?.getHours().toString(), // 时
        'H+': ndata?.getHours().toString(), // 时
        'm+': ndata?.getMinutes().toString(), // 分
        's+': ndata?.getSeconds().toString(), // 秒
        x: ndata?.valueOf().toString()
        // 有其他格式化字符需求可以继续添加，必须转化成字符串
    }
    for (const k in opt) {
        ret = new RegExp('(' + k + ')').exec(fmt)
        if (ret) {
            fmt = fmt.replace(
                ret[1],
                ret[1].length === 1
                    ? opt[k]
                    : opt[k]?.padStart(ret[1].length, '0')
            )
        }
    }
    return fmt
}

export function formatSecondsLength(value: number | string, local: boolean = true) {
    let secondTime = typeof (value) === 'string' ? parseInt(value) : value || 0
    let minuteTime = 0
    let hourTime = 0
    if (secondTime > 60) {
        minuteTime = Math.floor(secondTime / 60)
        secondTime = Math.floor(secondTime % 60)
    }
    if (minuteTime > 60) {
        hourTime = Math.floor(minuteTime / 60)
        minuteTime = Math.floor(minuteTime % 60)
    }
    const localName = ['小时', '分钟', '秒']

    if (local) {
        return [hourTime, minuteTime, secondTime].reduce((acc, cur, currentIndex) => {
            if (!(cur)) return acc
            acc += String(cur)
            acc += localName[currentIndex]
            return acc
        }, '')
    }

    return [hourTime, minuteTime, secondTime].reduce((acc, cur, currentIndex) => {
        // if (!(cur || acc)) return acc
        acc += String(cur).padStart(2, '0')
        if (currentIndex < 2) {
            acc += ':'
        }
        return acc
    }, '')
}

/**
 * 字符串转到驼峰
 * splitStr: 规制的字符串 -(默认) 或者_
 * type : 1 大驼峰（默认） 2 小驼峰
 */
export function stringToHump(
    str: string,
    splitStr: string = '-',
    type: number = 1
) {
    const arr = str.split(splitStr)
    let resStr = arr.reduce(function (prev, cur) {
        const str = prev + cur.slice(0, 1).toUpperCase() + cur.slice(1)
        return str
    })
    if (type == 1) {
        // 转小驼峰这一行不需要
        resStr = resStr.slice(0, 1).toUpperCase() + resStr.slice(1)
    }

    return resStr
}

/**
 * wait等待
 */
export function wait(timeout = 1000) {
    return new Promise((resolve) => {
        setTimeout(resolve, timeout)
    })
}

// 节流
function Shake() {
    let timer = -1
    return function (obj: () => void, millisecond = 300) {
        clearTimeout(timer)
        timer = setTimeout(obj, millisecond) as unknown as number
    }
}

export const shake = Shake()

// 防抖
function Debounce() {
    const keys = [] as string[]
    return function (obj: () => void, millisecond = 300, key = '') {
        if (keys.indexOf(key) !== -1) {
            return
        }
        obj()
        const index = keys.length
        keys[index] = key
        setTimeout(() => {
            keys.splice(index, 1)
        }, millisecond)
        return true
    }
}

export const debounce = Debounce()

export async function AwaitDebounce(
    millisecond = 300,
    key = ''
): Promise<void> {
    return new Promise((resolve, reject) => {
        const result = debounce(
            () => {
                resolve()
            },
            millisecond,
            key
        )
        if (!result) {
            reject()
        }
    })
}


export function downloadBlobFile(blobObject: Blob, fileName = 'excel.xlsx') {
    const blob = new Blob([blobObject], {
        type: blobObject.type
    })
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = fileName
    a.click()
    window.URL.revokeObjectURL(url)
}
/**
 * 
 * @param fileName 文件名 / 远程的文件路径
 * @param isZip 是否为zip包
 */
export function downloadFile(fileName: string, isZip = false) {
    const tempLink = document.createElement('a') // 创建a标签
    let downloadfileName = fileName
    // 非远程的文件路径 则拼接前缀
    if (!fileName.startsWith('http://') && !fileName.startsWith('https://')) {
        const type = isZip ? 'downloadZip' : 'download'
        const path = import.meta.env.VITE_APP_BASE_API || import.meta.env.VITE_APP_PROXY_API
        downloadfileName = `${path}/common/${type}?delete=true&fileName=${fileName}`
    }
    tempLink.href = downloadfileName
    tempLink.target = '_blank'
    tempLink.download = fileName || ''
    document.body.appendChild(tempLink)
    tempLink.click() // 点击下载
    document.body.removeChild(tempLink) // 下载完成移除元素
}

// 下载文本
export function downloadBlobString(
    data: string,
    filename: string,
    type: string
): void {
    const blob = new Blob([data])
    const tempLink = document.createElement('a') // 创建a标签
    const href = window.URL.createObjectURL(blob) // 创建下载的链接
    //filename
    const fileName = `${filename}.${type}`
    tempLink.href = href
    tempLink.target = '_blank'
    tempLink.download = fileName
    document.body.appendChild(tempLink)
    tempLink.click() // 点击下载
    document.body.removeChild(tempLink) // 下载完成移除元素
    window.URL.revokeObjectURL(href) // 释放掉blob对象
}

export function arrayToObject<T>(arr: T[], key: string) {
    const obj = {} as {
        [key: string]: T
    }
    arr.forEach((item) => {
        obj[
            (
                item as {
                    [key: string]: string
                }
            )[key]
        ] = item
    })
    return obj
}

export function objectToArray<T>(obj: { [key: string]: T }) {
    const arr = [] as T[]
    Object.keys(obj).forEach((objKey) => {
        arr.push(obj[objKey])
    })
    return arr
}

export function mapDatasets<K, V>(
    datasets: (V & Record<string, any>)[],
    key: string = 'value'
): Map<K, V> {
    const map = new Map<K, V>()
    if (!Array.isArray(datasets)) return map
    datasets.forEach((item) => {
        if (item[key] === undefined) return
        map.set(item[key], item)
    })
    return map
}

export function run(expression: string, params: object = {}): unknown {
    // return eval.call(params, expression)

    return Function(
        ...Object.keys(params),
        `
        "use strict";
        return (${expression})
    `
    )(...Object.values(params))
}

export function getVideoDuration(src: string): Promise<number> {
    return new Promise((resolve) => {
        const viedo = new Audio(src)
        viedo.addEventListener('loadedmetadata', () => {
            resolve(viedo.duration)
        })
    })
}


/**
 * 复制文本内容
 * @param content 文本内容
 * @returns result <boolean>
 */
export function copyText(content: string) {
    if (!content) {
        return Promise.reject()
    }
    // 设置想要复制的文本内容
    const text = content
    // 判断是否支持clipboard方式
    if (window.navigator.clipboard) {
        return window.navigator.clipboard.writeText(text)
    } else {
        // 不支持clipboard方式 则采用document.execCommand()方式
        // 创建一个input元素
        const inputDom = document.createElement('textarea')
        // 设置为只读 防止移动端手机上弹出软键盘
        inputDom.setAttribute('readonly', 'readonly')
        // 给input元素赋值
        inputDom.value = text
        // 将创建的input添加到body
        document.body.appendChild(inputDom)
        // 选中input元素的内容
        inputDom.select()
        // 执行浏览器复制命令
        // 复制命令会将当前选中的内容复制到剪切板中（这里就是创建的input标签中的内容）
        // Input要在正常的编辑状态下原生复制方法才会生效
        const result = document.execCommand('copy')
        // 判断是否复制成功
        document.body.removeChild(inputDom)
        if (result) {
            return Promise.resolve()
        } else {
            return Promise.reject()
        }
    }
}

export function imageToBase64(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = () => {
            resolve(reader.result as string)
        }
        reader.onerror = (error) => reject(error)
    })
}

export function getAssetsFile(url: string) {
    return new URL(`../assets/${url}`, import.meta.url).href
}

// 表单列表和表单模块用来判断当前的项该用input还是select展示
export function judgeItemType(type: string): string {
    const selectTypeList = ['select', 'multipleSelect']
    return selectTypeList.includes(type) ? 'select' : 'input'
}

// 获取收款账户
export async function getReceiptData(channelId: number) {
    const res = await getReceiptList({
        channelId,
        pageSize: 999,
        pageNum: 1
    })
    res?.list.forEach(i => {
        i.label = i.bankName
        i.value = i.id
    })
    return res?.list || []
}

// 通过公司名称获取公司列表
export async function getCompanyListByName(name: string): Promise<OptionDto[]> {
    if (!name) return []
    const res = await getCompanyList({
        companyName: name
    })
    return res?.map(i => {
        return {
            label: i.companyName,
            value: i.id
        }
    })
}

// 数字转中文大写
export function convertCurrency(money: number | string) {
    //汉字的数字
    const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
    //基本单位
    const cnIntRadice = ['', '拾', '佰', '仟']
    //对应整数部分扩展单位
    const cnIntUnits = ['', '万', '亿', '兆']
    //对应小数部分单位
    const cnDecUnits = ['角', '分', '毫', '厘']
    //整数金额时后面跟的字符
    const cnInteger = '整'
    //整型完以后的单位
    const cnIntLast = '元'
    //最大处理的数字
    // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
    const maxNum = 999999999999999.9999
    //金额整数部分
    let integerNum
    //金额小数部分
    let decimalNum
    //输出的中文金额字符串
    let chineseStr = ''
    //分离金额后用的数组，预定义
    let parts
    // 传入的参数为空情况 
    if (!money) {
        return ''
    }
    if (typeof money === 'string') {
        money = parseFloat(money)
    }
    if (money >= maxNum) {
        return ''
    }
    // 传入的参数为0情况 
    if (money == 0) {
        chineseStr = cnNums[0] + cnIntLast + cnInteger
        return chineseStr
    }
    // 转为字符串
    money = money.toString()
    // indexOf 检测某字符在字符串中首次出现的位置 返回索引值（从0 开始） -1 代表无
    if (money.indexOf('.') == -1) {
        integerNum = money
        decimalNum = ''
    } else {
        parts = money.split('.')
        integerNum = parts[0]
        decimalNum = parts[1].substr(0, 4)
    }
    //转换整数部分
    if (parseInt(integerNum, 10) > 0) {
        let zeroCount = 0
        const IntLen = integerNum.length
        for (let i = 0; i < IntLen; i++) {
            const n = integerNum.substr(i, 1)
            const p = IntLen - i - 1
            const q = p / 4
            const m = p % 4
            if (n == '0') {
                zeroCount++
            } else {
                if (zeroCount > 0) {
                    chineseStr += cnNums[0]
                }
                zeroCount = 0
                chineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
            }
            if (m == 0 && zeroCount < 4) {
                chineseStr += cnIntUnits[q]
            }
        }
        // 最后+ 元
        chineseStr += cnIntLast
    }
    // 转换小数部分
    if (decimalNum != '') {
        const decLen = decimalNum.length
        for (let i = 0; i < decLen; i++) {
            const n = decimalNum.substr(i, 1)
            if (n != '0') {
                chineseStr += cnNums[Number(n)] + cnDecUnits[i]
            }
        }
    }
    if (chineseStr == '') {
        chineseStr += cnNums[0] + cnIntLast + cnInteger
    } else if (decimalNum == '') {
        chineseStr += cnInteger
    }

    return chineseStr
}


export function paramsToString(params: Record<string, any>): string {
    let str = ''
    for (const key in params) {
        if (params[key] !== undefined && params[key] !== null) {
            str += `${key}=${params[key]}&`
        }
    }
    return str.substring(0, str.length - 1)
}



// 第一个参数 2024-09-11 00:00:00 根据第二个参数 拿到 前n天或者 后n天的日期 -n前 n后
export function calculateDate(baseDateStr: string, n: number) {
    const baseDate = new Date(baseDateStr) // 添加时间部分以确保兼容性

    // 计算偏移后的日期（毫秒）
    const offsetDate = new Date(baseDate.getTime() + n * 24 * 60 * 60 * 1000)

    // 格式化偏移后的日期为 "YYYY-MM-DD" 格式
    const year = offsetDate.getFullYear()
    const month = (offsetDate.getMonth() + 1).toString().padStart(2, '0') // 月份从 0 开始，所以需要 +1
    const day = offsetDate.getDate().toString().padStart(2, '0')

    return `${year}-${month}-${day}`
}


export function getBase64FromFile(file: UploadFileDto) {
    if (!file.base64) return ''
    return file.base64.split(';base64,')[1]
}


/**
 * 数字转字母从1开始 1 -> A 2 -> B
 * @param n 传入的数字
 * @param cap 是否大写
 * @returns string 字母
 */
export function numberToLetter(n: number, isCap: boolean = true) {
    if (typeof n !== 'number') return ''
    const code = ('' + n).charCodeAt(0)
    const step = isCap ? 16 : 48
    return String.fromCharCode(code + step)
}

export function confirm(content: string | (() => VueNode) | VueNode, title?: string, config?: ModalFuncProps): Promise<void> {
    return new Promise((resolve, reject) => {
        Modal.confirm({
            content: content,
            title: title,
            onOk: () => {
                resolve()
            },
            onCancel: () => {
                reject()
            },
            ...config
        })
    })
}


/**
 * 为给定列表中的每个元素及其子元素设置唯一的键值
 * 此函数旨在处理具有潜在嵌套结构的列表，并为每个元素生成一个键值，以便在Ant Design Tree组件中使用
 * 
 * @param list - 一个对象数组，每个对象可能包含一个children数组，表示进一步的嵌套结构
 * @param prop - 指定用作键值的属性名称
 * @returns 返回一个新数组，其中每个元素及其子元素都设置了键值
 */
export function createAntTreeKey<T extends {children?: T[]}>(list: T[], prop: string) {
    // 将输入列表转换为AntTreeDto<T>类型的数组，以便后续操作
    const res = [...list] as AntTreeDto<T>[]
    /**
     * 递归地为列表中的每个元素设置键值
     * 
     * @param list - 要设置键值的元素列表
     */
    function setKey(list: AntTreeDto<T>[]) {
        list.forEach((i) => {
            // 为当前元素设置键值
            (i).key = i[prop as keyof typeof i] as number
            // 如果当前元素有子元素，则递归调用setKey函数
            if (i.children) {
                setKey(i.children)
            }
        })
    }
    
    // 对复制后的列表应用设置键值的函数
    setKey(res)
    // 返回设置了键值的新列表
    return res
}


export function goBack() {
    history.go(-1)
}